package com.apchernykh.rozhlasapp.player;

import com.apchernykh.rozhlasapp.player.fallback.IFallbackPlayer;
import com.apchernykh.rozhlasapp.player.fallback.NullFallbackPlayer;
import com.apchernykh.rozhlasapp.player.fallback.RozhlasFallbackPlayerAAC;
import com.apchernykh.rozhlasapp.player.fallback.RozhlasFallbackPlayerMP3;
import com.apchernykh.rozhlasapp.player.fallback.RozhlasNativeFallbackPlayer;
import com.apchernykh.rozhlasapp.player.fallback.RozhlasProxyFallbackPlayer;
import com.apchernykh.rozhlasapp.utils.ConditionalLog;
import com.apchernykh.types.ApplicationModel;
import com.apchernykh.types.DetailedUrl;
import com.apchernykh.types.ApplicationModel.EPlaybackStatuses;
import android.content.Context;
import android.media.MediaPlayer;

public class MediaPlayerWrapper implements IPlayerWrapper,
		android.media.MediaPlayer.OnCompletionListener,
		android.media.MediaPlayer.OnPreparedListener,
		android.media.MediaPlayer.OnErrorListener,
		android.media.MediaPlayer.OnBufferingUpdateListener {

	final protected Context mContext;
	final IPlaybackCallbacks mParent;
	int mFallbackTryCount = guessFallbackTypeByPhoneModel();
	private int mSeekTo;
	private boolean mPrepared;
	private long mPrepareDate = 0;
	static private long mRetryTimeout = 100000;
	IFallbackPlayer mFBPlayer = new NullFallbackPlayer();

	public MediaPlayerWrapper(IPlaybackCallbacks iService) {
		mContext = (Context) iService;
		mParent = iService;
	}

	private int guessFallbackTypeByPhoneModel() {
		if("GT-I9300".equals(android.os.Build.MODEL))
		{
			ConditionalLog.e("Recognized SGS III");
			return 2;
		}
		
		if("sdk".equals(android.os.Build.MODEL))
		{
			ConditionalLog.e("Recognized emulator");
			return 1;
		}
		return 0;
	}

	private void notifyStopPlayback() {
		mParent.onPlaybackStatusChanged(EPlaybackStatuses.STOPPED);
	}

	private void notifyConnectionInProgress() {
		mParent.onPlaybackStatusChanged(EPlaybackStatuses.CONNECTION_IN_PROGRESS);
	}

	private void notifyStartPlayback() {
		mParent.onPlaybackStatusChanged(EPlaybackStatuses.PLAYING);
	}

	DetailedUrl mCurrentURL = null;

	public void stop() {
		if (mFBPlayer.isValid()) {
			mFallbackTryCount = guessFallbackTypeByPhoneModel(); //TODO: restore from preferences.
			mFBPlayer.stop();
			mFBPlayer = new NullFallbackPlayer();
			mParent.goStopYourself();
			notifyStopPlayback();
		}
	}

	@Override
	public void pause() {
		// TODO distinguish podcasts which can be resumed from stations which
		// cannot
		if (mFBPlayer.isValid()) {
			mFBPlayer.stop();
			mFBPlayer = new NullFallbackPlayer();
			mParent.onPlaybackStatusChanged(EPlaybackStatuses.PAUSED_WAIT_PHONE_HANG);
		} else {
			notifyStopPlayback();
		}
	}

	@Override
	public String play(DetailedUrl iURL, int iSeekTo) {
		mCurrentURL = iURL;
		mSeekTo = iSeekTo;
		notifyConnectionInProgress();
		startFallbackPlayer();
		return "Playback";
	}

	protected IFallbackPlayer instantiateFileBasedPlayer(String iURL) {
		if (iURL.endsWith("mp3")) {
			return new RozhlasFallbackPlayerMP3(mContext, iURL);
		} else {
			return new RozhlasFallbackPlayerAAC(mContext, iURL);
		}
	}

	protected IFallbackPlayer instantiateFallbackPlayer(String iURL,
			int iPlayerType) {
		IFallbackPlayer aPlayerSelected;
		// We are given 3 attempts to instantiate a player, which will not
		// return an error on prepare
		// they go in the nativity order - Transparent native, proxy, file-based

		switch (iPlayerType) {
		case 0:
			aPlayerSelected = new RozhlasNativeFallbackPlayer(mContext, iURL);
			break;
		case 1:
			aPlayerSelected = new RozhlasProxyFallbackPlayer(mContext, iURL);
			break;
		case 2:
			aPlayerSelected = instantiateFileBasedPlayer(iURL);
			break;
		default:
			aPlayerSelected = new NullFallbackPlayer();
		}
		ConditionalLog.e(
				"player wrapper instantiated " + String.valueOf(aPlayerSelected));
		return aPlayerSelected;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * android.media.MediaPlayer.OnErrorListener#onError(android.media.MediaPlayer
	 * , int, int)
	 */
	public boolean onError(MediaPlayer iPlayer, int iWhat, int iExtra) {
			ConditionalLog.e( "playback error occured");
		long aCurrentDate = System.currentTimeMillis();
		if (mPrepared && mPrepareDate + mRetryTimeout < aCurrentDate) {
			ConditionalLog.e(
					ApplicationModel.TAG,
					"stopping playback since it does not look like a player bug ["
							+ String.valueOf(mPrepared)
							+ ":"
							+ String.valueOf(mPrepareDate + mRetryTimeout
									- aCurrentDate) + "]");
			stop();
		} else {
			ConditionalLog.e("will workaround media player bug ["
							+ String.valueOf(mPrepared)
							+ ":"
							+ String.valueOf(mPrepareDate + mRetryTimeout
									- aCurrentDate) + "]");
			
			mFallbackTryCount++;
			mFBPlayer.dispose();
			mFBPlayer = new NullFallbackPlayer();
			mParent.onPleaseRetry(mCurrentURL);
		}
		return true;
	}

	private void startFallbackPlayer() {
		mFBPlayer = instantiateFallbackPlayer(mCurrentURL.mUrl,
				mFallbackTryCount);
		if (!mFBPlayer.isValid()) {
			// Ran out of retries
			notifyStopPlayback();
			mFallbackTryCount = 0; // reset retry counter
			return;
		}
		mFBPlayer.setOnErrorListener(this);
		mFBPlayer.setOnPreparedListener(this);
		mFBPlayer.setOnCompletionListener(this);
		mFBPlayer.setOnBufferingUpdateListener(this);
		mFBPlayer.start();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * android.media.MediaPlayer.OnCompletionListener#onCompletion(android.media
	 * .MediaPlayer)
	 */
	public void onCompletion(MediaPlayer iPlayer) {
		stop();
	}

	public void onPrepared(MediaPlayer iPlayer) {
		mPrepared = true;
		mPrepareDate = System.currentTimeMillis();
		notifyStartPlayback();
	}

	@Override
	public boolean isActive() {
		return mFBPlayer.isValid();
	}

	@Override
	public void onBufferingUpdate(MediaPlayer iPlayer, int iPercent) {
		mParent.onPrepareProgressChanged(iPercent);
	}

	@Override
	public int getCurrentPosition() {
		throw new NullPointerException("Not implemented");
	}

}
